
#include "SimpleBlobsLib.h"

#include <string>
#include <vector>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <iostream>
#include "Utils.h"
#include "Templates.h"

//#include "Blobs\BlobResult.h"
#include "cvBlob.h"


void aplicar_threshold(const cv::Mat &img,cv::Mat &imgSaida)
{
	cv::threshold(img,imgSaida,100,255,cv::THRESH_BINARY_INV);

	int t = imgSaida.type();
}


bool analisa_vizinhanca_x(const cv::Mat& img,const cv::Point pos)
{
	const int number_of_directions = 2;
	const int initial_pos[number_of_directions] = {pos.x,pos.x};
	const int final_pos[number_of_directions] = {initial_pos[0]+6,initial_pos[1]-6};
	const int step[number_of_directions] = {1,-1};
	int pixels_ligados[number_of_directions]= { 0 };

	for (int i = 0 ; i < number_of_directions;i++)
	{
		for(int x = initial_pos[i]; x != final_pos[i] ;x+=step[i])
		{
			//std::cout << "x=> " <<x << std::endl;
			//std::cout << "y = " <<pos.y << std::endl;

			if(x < 0 || x>= img.cols || img.at<uchar>(cv::Point(x,pos.y))==0)
				break;
			pixels_ligados[i]++;
		}
	}

	const int min_pixels_ligados = 5;

	return pixels_ligados[0] + pixels_ligados[1] >= min_pixels_ligados;
}


bool analisa_vizinhanca_y(const cv::Mat& img,const cv::Point pos)
{
	const int number_of_directions = 2;
	const int initial_pos[number_of_directions] = {pos.y,pos.y};
	const int final_pos[number_of_directions] = {initial_pos[0]+6,initial_pos[1]-6};
	const int step[number_of_directions] = {1,-1};
	int pixels_ligados[number_of_directions]= { 0 };
	
	for (int i = 0 ; i < number_of_directions;i++)
	{
		for(int y = initial_pos[i]; y != final_pos[i] ;y+=step[i])
		{
			if((y <0 || y >= img.rows) || img.at<uchar>(cv::Point(pos.x,y))==0)
				break;
			pixels_ligados[i]++;
		}
	}

	const int min_pixels_ligados = 5;

	return pixels_ligados[0] + pixels_ligados[1] >= min_pixels_ligados;
}


void remove_ruido(const cv::Mat&img,cv::Mat &imgSaida)
{
	//cv::medianBlur(img,imgSaida,5);
	//cv::Mat imgTmp;
	//cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ERODE,cv::Size(3,3));
	//cv::erode(img,imgTmp,kernel);
	//cv::erode(imgTmp,imgSaida,kernel);

	imgSaida.create(img.rows,img.cols,img.type());

	try
	{
		for(int x=0; x<img.cols; x++)
		{
			for(int y=0; y<img.rows; y++)
			{
				byte pixel = img.at<uchar>(cv::Point(x,y));
						
				if( pixel == 0 || y <= 5 || y >=img.cols-5)
				{
					imgSaida.at<uchar>(cv::Point(x,y)) = 0;
				}
				else
				{
					if(analisa_vizinhanca_x(img,cv::Point(x,y)) && 
					   analisa_vizinhanca_y(img,cv::Point(x,y)))
					{
						imgSaida.at<uchar>(cv::Point(x,y)) = 255;
					}
					else
					{
						imgSaida.at<uchar>(cv::Point(x,y)) = 0;
					}
				}
			}
		}
	}catch(std::exception &ex)
	{
		std::cout << ex.what();
	}
		
	

	cv::imshow("captcha",imgSaida);

	cv::waitKey();

			
//		    std::cout << img.at<uchar>(i,j) << " " << img.at<uchar>(i,j) << " " << img.at<uchar>(i,j) << std::endl;


}
/*
int localiza_blobs(const cv::Mat & img)
{
	CBlobResult blobs;
	int i;
	CBlob *currentBlob;
	//cv::Mat invertedImage = (255 - img);
	IplImage iplimg = img;
	// find blobs in image
	blobs = CBlobResult(&iplimg, NULL, 0 );
	//blobs.Filter( blobs, B_EXCLUDE, CBlobGetArea(), B_LESS, 15 );CBlobGetDiffY
	blobs.ClearBlobs();

	blobs.Filter( blobs, B_INCLUDE, CBlobGetDiffY(), B_GREATER, 1 );	
	blobs.Filter( blobs, B_INCLUDE, CBlobGetDiffX(), B_GREATER, 1 );

	IplImage*	displayedImage = cvCreateImage(cvGetSize(&iplimg), IPL_DEPTH_8U,3);

	// display filtered blobs
	cvMerge( &iplimg, &iplimg, &iplimg, NULL, displayedImage );

	int numblobs = blobs.GetNumBlobs();

	for (i = 0; i < blobs.GetNumBlobs(); i++ )
	{
		currentBlob = blobs.GetBlob(i);
		
		int color = 100 + i*25;
		currentBlob->FillBlob( displayedImage, CV_RGB(color,color,color));

		
		cvShowImage( "123", displayedImage );
		cvWaitKey(0);
	}
	
	

	return 1;
}

*/
/*
int localiza_blobs2(const cv::Mat &img)
{

	using namespace cvb;


	try
	{
		CvBlobs blobs;
		IplImage iplimg = img;
		IplImage *labelImg=cvCreateImage(cvGetSize(&iplimg), IPL_DEPTH_LABEL, 1);
		IplImage *imgShow=cvCreateImage(cvGetSize(&iplimg), IPL_DEPTH_8U, 3);
		unsigned int result=cvLabel(&iplimg,labelImg, blobs);
		//Now you can simply render the blobs in the original image:

		cvRenderBlobs(labelImg, blobs, imgShow, imgShow);

	
		for(auto blob = blobs.begin();blob!=blobs.end();blob++)
		{
			//std::cout << blob->second.
		}
	
		cvShowImage( "123", imgShow );
		cvWaitKey(0);
	
	}
	catch(std::exception &ex)
	{
		std::cout << ex.what();
	}
	return 1;
}
*/

int localiza_blobs3(const cv::Mat &img,std::vector<cv::Rect>&blobRects)
{

	Blobs::BlobList blobs;

	blobs.FindBlobs(img,0);

	blobs.Exclude(Blobs::BY_WIDTH,10);

	blobs.Split(Blobs::BY_WIDTH,13,30);

	//cv::Mat imgPaint  = img.clone();
	//blobs.PaintBlobRects(imgPaint);
		
	//blobs.GetBlobRects();


	return 1;
}

std::string reconhece_caracteres1(const cv::Mat&img,const std::vector<cv::Point>&blobs,TemplateMatching::Templates & templates)
{
	
	cv::Size maxTemplateSize = templates.GetMaximumTemplateSize();
	
	if(maxTemplateSize.width%2==0)
		maxTemplateSize.width = maxTemplateSize.width +1;
	if(maxTemplateSize.height%2==0)
		maxTemplateSize.height = maxTemplateSize.height +1;

	cv::Mat resultImg(maxTemplateSize.width,maxTemplateSize.height,0);
	
	typedef std::map<int,double>  MapaRespostas;	
	typedef std::pair<int,double>  ParIndiceResposta;
	typedef std::vector<ParIndiceResposta>  VetorRespostas;

	std::string resposta = "";
	for(size_t i = 0 ; i < blobs.size(); i++)
	{
		VetorRespostas respostas;
		cv::Rect charRoi (blobs[i].x-maxTemplateSize.width/2,blobs[i].y-maxTemplateSize.height/2,maxTemplateSize.width,maxTemplateSize.height);

		resultImg = cv::Mat::zeros(maxTemplateSize.width,maxTemplateSize.height,0);
		for(size_t ti = 0 ; ti < templates.GetNumberOfTemplates();ti++)
		{		
			auto templateImg = templates.GetImage(ti);			
			
			cv::matchTemplate(cv::Mat(img,charRoi),templateImg,resultImg,CV_TM_CCORR);

			cv::imwrite("d:\\"+ std::to_string((long long)ti)+".bmp",resultImg);
			double min,max;
			cv::minMaxLoc(resultImg,&min,&max);

			respostas.push_back(ParIndiceResposta(ti,max));
			
		}
		
		std::sort(respostas.begin(),respostas.end(),[](const ParIndiceResposta&p1,const ParIndiceResposta&p2){return p1.second < p2.second;});

		resposta += templates.GetChar(respostas.rbegin()->first);
	}
	return resposta;
}


int xmain( int argc, char** argv )
{
	if(argc < 2)
	{
		return -1;
	}


	std::vector<std::string> args(argv, argv + argc);

	std::vector<std::string>files;
	Utils::GetFilesInDirectory(args[1],"*.BMP",files);

	if(files.size()==0)
		files.push_back(args[1]);

	TemplateMatching::Templates templates(Utils::GetApplicationDirectory() + "templates");
	for(size_t i = 0 ; i < files.size();i++)
	{
		auto &file = files[i];
		cv::Mat imagem = cv::imread(file,0),imgProc[3];


		::aplicar_threshold(imagem,imgProc[0]);
	
		cv::imshow("captcha",imgProc[0]);

		cv::waitKey();
		::remove_ruido(imgProc[0],imgProc[1]);
		

		std::vector<cv::Rect>blobsRects;
		::localiza_blobs3(imgProc[1],blobsRects);
		



		::reconhece_caracteres1(imgProc[1],Utils::GetVectorOfCenters(blobsRects),templates);

	}

}